import 'dart:convert';
import 'dart:io';

import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:fluttertoast/fluttertoast.dart';
import 'package:grasp_flutter_app/utils/local/local_string_base.dart';
import 'package:grasp_flutter_app/utils/redux/theme_redux.dart';
import 'package:path_provider/path_provider.dart';
import 'package:permission_handler/permission_handler.dart';
import 'package:redux/redux.dart';
import 'package:event_bus/event_bus.dart';
import 'package:path/path.dart' as path;
import 'event/http_error_event.dart';
import 'local/app_localizations.dart';
import 'redux/locale_redux.dart';
import 'redux/sys_state.dart';
import 'styles.dart';
import 'view/BootomInputBorder.dart';

// 导出
export 'common.dart';
export 'consts.dart';
export 'styles.dart';
export 'config.dart';
export 'size_utils.dart';
export 'redux/sys_state.dart';
export 'redux/locale_redux.dart';
export 'local/local_storage.dart';

export 'event/http_error_event.dart';
export 'net/result_data.dart';
export 'net/api.dart';

export 'view/DialogEx.dart';
export 'view/IconText.dart';
export 'view/GButton.dart';
export 'view/DrapDownSheet.dart';

export '../plugins/drapdown/dropdown_menu.dart';

export 'package:sprintf/sprintf.dart';
export 'package:redux/redux.dart';
export 'package:line_icons/line_icons.dart';
export 'package:cached_network_image/cached_network_image.dart';
export 'package:flutter_redux/flutter_redux.dart';

EventBus eventBus = new EventBus();

class Utils {
  static Locale curLocale;
  static String _downloadPath;

  static toast(String msg) {
    Fluttertoast.showToast(msg: msg);
  }

  /// 获取下载目录
  static Future<String> getDownloadsPath() async {
    if (Platform.isIOS)
      return (await getApplicationDocumentsDirectory()).path;
    else {
      if (_downloadPath == null) {
        _downloadPath = (await getExternalStorageDirectory()).path;
        if (! (existPath(_downloadPath)))
          _downloadPath = (await getTemporaryDirectory()).path;
      }
      print("downloadPath: $_downloadPath");
      return _downloadPath;
    }
  }

  /// 获取本地路径
  static getLocalPath() async {
    Directory appDir;
    if (Platform.isIOS) {
      appDir = await getApplicationDocumentsDirectory();
    } else {
      appDir = await getExternalStorageDirectory();
    }
    PermissionStatus permission = await PermissionHandler()
        .checkPermissionStatus(PermissionGroup.storage);
    if (permission != PermissionStatus.granted) {
      Map<PermissionGroup, PermissionStatus> permissions =
      await PermissionHandler()
          .requestPermissions([PermissionGroup.storage]);
      if (permissions[PermissionGroup.storage] != PermissionStatus.granted) {
        return null;
      }
    }
    String appDocPath = appDir.path + "/graspapp";
    Directory appPath = Directory(appDocPath);
    await appPath.create(recursive: true);
    return appPath;
  }

  static LocalStringBase getLocale(BuildContext context) {
    return AppLocalizations.of(context).currentLocalized;
  }

  static pushTheme(Store store, int index) {
    ThemeData themeData;
    List<Color> colors = getThemeListColor();
    themeData = getThemeData(colors[index]);
    Styles.primaryValue = colors[index];
    Styles.primaryContrastValue = themeData.primaryTextTheme.title.color;
    store.dispatch(new RefreshThemeDataAction(themeData));
  }

  static getThemeData(Color color) {
    return ThemeData(
      primarySwatch: color,
      platform: TargetPlatform.android,
      inputDecorationTheme: new InputDecorationTheme(
        border: new BootomInputBorder(Styles.lineColor, width: Styles.borderSize),
      ),
    );
  }

  static List<Color> getThemeListColor() {
    return Styles.defaultThemeListColor;
  }

  /// 切换语言
  static changeLocale(Store<SysState> store, int index) {
    Locale locale = store.state.platformLocale;
    switch (index) {
      case 1:
        locale = Locale('zh', 'CH');
        break;
      case 2:
        locale = Locale('en', 'US');
        break;
    }
    curLocale = locale;
    store.dispatch(RefreshLocaleAction(locale));
  }

  static const IMAGE_END = [".png", ".jpg", ".jpeg", ".gif", ".svg"];

  static isImageEnd(path) {
    bool image = false;
    for (String item in IMAGE_END) {
      if (path.indexOf(item) + item.length == path.length) {
        image = true;
      }
    }
    return image;
  }

  /// 复制到剪粘板
  static copy(String data, BuildContext context) {
    Clipboard.setData(new ClipboardData(text: data));
    toast(Utils.getLocale(context).copySuccess);
  }

  /// 返回当前时间戳
  static int currentTimeMillis() {
    return new DateTime.now().millisecondsSinceEpoch;
  }

  /// 隐藏软键盘
  static void hideSoftKey() {
    SystemChannels.textInput.invokeMethod('TextInput.hide');
  }

  /// 显示软键盘
  static void showSoftKey() {
    SystemChannels.textInput.invokeMethod('TextInput.show');
  }

  /// 连接字符串
  static String linkStr([final String a, final String b, final String c]) {
    return check(a) + check(b) + check(c);
  }

  /// 字符串查询：没找到或原串为null返回 -1
  static int strPos(final String src, final String pattern,
      [int start = 0, bool ignoreCase = false]) {
    if (src == null || pattern == null) return -1;
    if (ignoreCase)
      return src.toLowerCase().indexOf(pattern.toLowerCase(), start);
    return src.indexOf(pattern, start);
  }

  static const List<String> STimeUtils = ["天", "小时", "分钟", "刚刚", "今天", "昨天"];

  /// 返回指定时间对于当前时间相差的时间字符串（如：1小时前，5天前）
  static String timeBetween(DateTime start,
      [String suffix = "",
        DateTime curTime,
        List<String> utils = STimeUtils]) {
    if (start == null) return "";
    int cur = (curTime == null)
        ? DateTime.now().millisecondsSinceEpoch
        : curTime.millisecondsSinceEpoch;
    int v = cur - start.millisecondsSinceEpoch;
    if (v ~/ 1000 < 60) return utils[3];
    if (v ~/ 1000 < 3600) return "${v ~/ (1000 * 60)}${utils[2]}$suffix";
    if (v ~/ 1000 < 86400) return "${v ~/ (1000 * 3600)}${utils[1]}$suffix";
    v = v ~/ (1000 * 86400);
    if (v > 7) return Utils.dateToStr(start);
    return "$v${utils[0]}$suffix";
  }

  /// 返回指定时间对于当前时间相差的天数字符串（如：5天前）
  static String timeBetweenDay(DateTime start,
      [String suffix = "",
        DateTime curTime,
        List<String> utils = STimeUtils]) {
    if (start == null) return "";
    int cur = (curTime == null)
        ? DateTime.now().millisecondsSinceEpoch
        : curTime.millisecondsSinceEpoch;
    int v = cur - start.millisecondsSinceEpoch;
    v = v ~/ (1000 * 86400);
    if (v == 0) return "${utils[4]}";
    if (v == 1) return "${utils[5]}";
    if (v > 7) return Utils.dateToStr(start);
    return "$v${utils[0]}$suffix";
  }

  /// 返回两个日期相差的天数
  static int daysBetween(DateTime a, DateTime b, [bool ignoreTime = false]) {
    if (ignoreTime) {
      int v = a.millisecondsSinceEpoch ~/ 86400000 -
          b.millisecondsSinceEpoch ~/ 86400000;
      if (v < 0) return -v;
      return v;
    } else {
      int v = a.millisecondsSinceEpoch - b.millisecondsSinceEpoch;
      if (v < 0) v = -v;
      return v ~/ 86400000;
    }
  }

  /// 获取屏幕宽度
  static double getScreenWidth(BuildContext context) {
    return MediaQuery.of(context).size.width;
  }

  /// 获取屏幕高度
  static double getScreenHeight(BuildContext context) {
    return MediaQuery.of(context).size.height;
  }

  /// 获取系统状态栏高度
  static double getSysStatsHeight(BuildContext context) {
    return MediaQuery.of(context).padding.top;
  }

  static const RollupSize_Units = ["GB", "MB", "KB", "B"];

  /// 返回文件大小字符串
  static String getRollupSize(int size) {
    int idx = 3;
    int r1 = 0;
    String result = "";
    while (idx >= 0) {
      int s1 = size % 1024;
      size = size >> 10;
      if (size == 0 || idx == 0) {
        r1 = (r1 * 100) ~/ 1024;
        if (r1 > 0) {
          if (r1 >= 10)
            result = "$s1.$r1${RollupSize_Units[idx]}";
          else
            result = "$s1.0$r1${RollupSize_Units[idx]}";
        } else
          result = s1.toString() + RollupSize_Units[idx];
        break;
      }
      r1 = s1;
      idx--;
    }
    return result;
  }

  /// 路径分隔符
  static String get pathSeparator => Platform.pathSeparator;



  static errorHandleFunction(code, message, noTip) {
    if(noTip) {
      return message;
    }
    eventBus.fire(new HttpErrorEvent(code, message));
    return message;
  }

  /// 公共打开方式
  static navigatorRouter(BuildContext context, Widget widget) {
    return Navigator.push(context,
        new CupertinoPageRoute(builder: (context) => pageContainer(widget)));
  }

  /// Page页面的容器
  static Widget pageContainer(widget) {
    return MediaQuery(
      ///不受系统字体缩放影响
      data: MediaQueryData.fromWindow(WidgetsBinding.instance.window).copyWith(textScaleFactor: 1),
      child: widget);
  }

  /// 检测路径是否存在
  static bool existPath(final String _path) {
    return new Directory(_path).existsSync();
  }

  /// 提取文件路径, 结尾包含路径分隔符
  static String getFilePath(final String file) {
    return path.dirname(file) + Platform.pathSeparator;
  }

  /// 提取文件名（不包含路径和扩展名）
  static String getFileName(final String file) {
    return path.basenameWithoutExtension(file);
  }

  /// 提取文件扩展名
  static String getFileExt(final String file) {
    return path.extension(file);
  }

  /// 提取文件名（包含扩展名）
  static String getFileFullName(final String file) {
    return path.basename(file);
  }

  /// 创建目录
  static Future<bool> createPath(final String path) async {
    return (await new Directory(path).create(recursive: true)).exists();
  }

  /// 创建文件, 如果目录不存在会尝试创建目录
  static Future<File> createFile(final String file) async {
    try {
      String path = getFilePath(file);
      if (!existPath(path)) {
        if (!await createPath(path)) {
          return null;
        }
      }
      return await new File(file).create(recursive: true);
    } catch (e) {
      print(e);
      return null;
    }
  }

  /// 删除文件
  static Future<bool> deleteFile(final String file) async {
    try {
      await new File(file).delete(recursive: true);
      return true;
    } on Exception catch (e) {
      print(e);
      return false;
    }
  }

  /// 检测字符串，如果为null则返为空，否则返回原串
  static String check(String src, [String defaultValue = ""]) {
    if (src == null) return defaultValue;
    return src;
  }

  /// 日期转为字符串
  static String dateToStr(DateTime v, [bool isTwoDigits = false]) {
    if (v == null) return "";
    if (isTwoDigits)
      return "${v.year}-${twoDigits(v.month)}-${twoDigits(v.day)}";
    else
      return "${v.year}-${twoDigits(v.month)}-${v.day}";
  }

  /// 返回指定日期偏移指定天数后的日期
  static DateTime dateIncDay(DateTime date, int days) {
    return DateTime.fromMillisecondsSinceEpoch(date.millisecondsSinceEpoch + days * 86400000);
  }

  /// 获取指定年月的总天数
  static int getDateCount(int year, int month) {
    if (const <int>[1, 3, 5, 7, 8, 10, 12].contains(month)) {
      return 31;
    } else if (month == 2) {
      if ((year % 4 == 0 && year % 100 != 0) || year % 400 == 0)
        return 29;
      return 28;
    }
    return 30;
  }

  /// 获取指定时间偏移指定月份后的年月日期范围
  static DateTimeRange getMonthDateRange({DateTime cur, int offsetMonty = 1}) {
    if (cur == null) cur = DateTime.now();
    int m, y;
    if (offsetMonty <= 0) {
      m = cur.month - (-offsetMonty % 12);
      y = cur.year - (m <= 0 ? 1 : 0);
      if (m <= 0) m = 12 + m;
    } else {
      m = cur.month + (offsetMonty % 12);
      y = cur.year + (m > 12 ? 1 : 0);
      if (m > 12) m = m % 12;
    }
    return DateTimeRange(
        DateTime(y, m),
        DateTime(y, m, getDateCount(y, m), 23, 59, 59, 999)
    );
  }

  /// 获取指定时间偏移指定天数后的年月日期范围
  static DateTimeRange getDayDateRange({DateTime cur, int offsetDays = 0}) {
    if (cur == null) cur = DateTime.now();
    if (offsetDays != 0)
      cur = cur.add(Duration(days: offsetDays));
    int y = cur.year;
    int m = cur.month;
    int d = cur.day;
    return DateTimeRange(
        DateTime(y, m, d),
        DateTime(y, m, d, 23, 59, 59, 999)
    );
  }

  /// 返回指定日期偏移指定月数后的日期
  static DateTime dateIncMonthWithYearMonth(int year, int month, int incMonth) {
    if (incMonth > 0) {
      while (incMonth != 0) {
        if (month > 12) {
          year++;
          month = 1;
        } else
          month++;
        incMonth--;
      }
    } else {
      while (incMonth != 0) {
        if (month == 1) {
          year--;
          month = 12;
        } else
          month--;
        incMonth++;
      }
    }
    return DateTime.utc(year, month);
  }

  /// 返回指定日期偏移指定月数后的日期
  static DateTime dateIncMonth(DateTime v, int month) {
    int ly = v.year;
    int lm = v.month;
    if (month > 0) {
      while (month != 0) {
        if (lm > 12) {
          ly++;
          lm = 1;
        } else
          lm++;
        month--;
      }
    } else {
      while (month != 0) {
        if (lm == 1) {
          ly--;
          lm = 12;
        } else
          lm--;
        month++;
      }
    }
    return DateTime.utc(ly, lm, v.day, v.hour, v.minute, v.second);
  }

  /// 浮点数转字符串
  static String floatToStr(final double v, [int fractionDigits = 2]) {
    if (v == null)
      return "0.00";
    return v.toStringAsFixed(fractionDigits);
  }

  static String twoDigits(int n) {
    if (n >= 10) return "$n";
    return "0$n";
  }

  /// 日期时间转化为字符串
  static String dateTimeToStr(DateTime v, [bool cn = false]) {
    if (cn)
      return "${v.month}月${v.day}日 ${twoDigits(v.hour)}:${twoDigits(v.minute)}";
    else
      return "${v.year}-${twoDigits(v.month)}-${twoDigits(v.day)} ${twoDigits(v.hour)}:${twoDigits(v.minute)}:${twoDigits(v.second)}";
  }

  /// 字符串转为日期时间
  static DateTime strToDateTime(String str, [DateTime defaultValue]) {
    try {
      return DateTime.parse(str);
    } catch (e) {
      return defaultValue;
    }
  }

  /// 字符串转为整数
  static int strToInt(String str, [int defaultValue = 0]) {
    try {
      return int.parse(str);
    } catch (e) {
      return defaultValue;
    }
  }

  /// 字符串转为整数
  static double strToFloat(String str, [double defaultValue = 0.0]) {
    try {
      return double.parse(str);
    } catch (e) {
      return defaultValue;
    }
  }

  /// 安全的将对象转为浮点数
  static double toFloat(Object v, [double defaultValue = 0.0]) {
    if (v == null) return defaultValue;
    try {
      if (v is double)
        return v;
      return double.parse(v.toString());
    } catch (e) {
      return defaultValue;
    }
  }

  /// 开始一个页面
  static void startPage(BuildContext context, StatefulWidget page) {
    if (page == null) return;
    Navigator.push(context, new MyCustomRoute(builder: (_) => page));
  }

  /// 开始一个页面，并等待结束
  static Future<Object> startPageWait(
      BuildContext context, StatefulWidget page) async {
    if (page == null) return null;
    return await Navigator.push(
        context, new MyCustomRoute(builder: (_) => page));
  }

  /// 解析JSON，生成MAP对象
  static Map parseJson(final String data) {
    return JsonDecoder().convert(data);
  }

  /// 解析JSON，生成List对象
  static List parseJsonAsList(final String data) {
    return JsonDecoder().convert(data);
  }

  /// 将Map对象转为Json字符串
  static String jsonToString(final Map data) {
    return JsonEncoder().convert(data);
  }

  /// 将Map对象转为Json字符串
  static String jsonListToString(final List data) {
    return JsonEncoder().convert(data);
  }

  /// 计算总页数
  static int getPageCount(final int dataTotal, pageSize) {
    int v = dataTotal ~/ pageSize;
    if (v * pageSize < dataTotal)
      v++;
    if (v < 1) return 1;
    return v;
  }

}

class MyCustomRoute<T> extends MaterialPageRoute<T> {
  MyCustomRoute({WidgetBuilder builder, RouteSettings settings})
      : super(builder: builder, settings: settings);

  @override
  Widget buildTransitions(BuildContext context, Animation<double> animation,
      Animation<double> secondaryAnimation, Widget child) {
    if (settings.isInitialRoute) return child;
    return new FadeTransition(opacity: animation, child: child);
  }
}

class DateTimeRange {
  DateTime begin;
  DateTime end;
  DateTimeRange([this.begin, this.end]);
  bool get isEmpty => begin == null || end == null;
}